#include "WavHelper.h"
#include "dr_wav.h"

void wavWrite_f16(const char *filename, drwav_int16 *buffer, int sampleRate, uint32_t totalSampleCount, uint32_t channels)
{
    drwav_data_format format;
    format.container = drwav_container_riff;
    format.format = DR_WAVE_FORMAT_PCM;
    format.channels = channels;
    format.sampleRate = (drwav_uint32)sampleRate;
    format.bitsPerSample = 16;
    drwav *pWav = drwav_open_file_write(filename, &format);
    if (pWav)
    {
        drwav_uint64 samplesWritten = drwav_write(pWav, totalSampleCount, buffer);
        drwav_uninit(pWav);
        if (samplesWritten != totalSampleCount)
        {
            fprintf(stderr, "write file [%s] error.\n", filename);
            exit(1);
        }
    }
}

drwav_int16 *wavRead_f16(const char *filename, uint32_t *sampleRate, uint64_t *sampleCount, uint32_t *channels)
{
    drwav_uint64 totalSampleCount = 0;
    drwav_int16 *input = drwav_open_file_and_read_pcm_frames_s16(filename, channels, sampleRate, &totalSampleCount);
    *sampleCount = totalSampleCount * (*channels);
    return input;
}

uint64_t Resample_s16(const int16_t *input, int16_t *output, int inSampleRate, int outSampleRate, uint64_t inputSize,
    uint32_t channels
    ) {
    if (input == NULL)
        return 0;
    uint64_t outputSize = inputSize * outSampleRate / inSampleRate;
    if (output == NULL)
        return outputSize;
    double stepDist = ((double)inSampleRate / (double)outSampleRate);
    const uint64_t fixedFraction = (1LL << 32);
    const double normFixed = (1.0 / (1LL << 32));
    uint64_t step = ((uint64_t)(stepDist * fixedFraction + 0.5));
    uint64_t curOffset = 0;
    for (uint32_t i = 0; i < outputSize; i += 1) {
        for (uint32_t c = 0; c < channels; c += 1) {
            *output++ = (int16_t)(input[c] + (input[c + channels] - input[c]) * (
                (double)(curOffset >> 32) + ((curOffset & (fixedFraction - 1)) * normFixed)
                )
                );
        }
        curOffset += step;
        input += (curOffset >> 32) * channels;
        curOffset &= (fixedFraction - 1);
    }
    return outputSize;
}

void resampler(const char *in_file, const char *out_file, uint32_t targetSampleRate)
{
    uint32_t sampleRate = 0;
    uint64_t sampleCount = 0;
    uint32_t channels = 0;
    drwav_int16 *input = wavRead_f16(in_file, &sampleRate, &sampleCount, &channels);

    uint64_t targetSampleCount = Resample_s16(input, 0, sampleRate, targetSampleRate, sampleCount, channels);
    if (input)
    {
        drwav_int16 *output = (drwav_int16 *)malloc(targetSampleCount * sizeof(drwav_int16));
        if (output)
        {
            double startTime = now();
            Resample_s16(input, output, sampleRate, targetSampleRate, sampleCount / channels, channels);
            double time_interval = calcElapsed(startTime, now());
            wavWrite_f16(out_file, output, targetSampleRate, (uint32_t)targetSampleCount, channels);
            free(output);
        }
        free(input);
    }
}